package tech.yixiyun.framework.kuafu.db.session;

import tech.yixiyun.framework.kuafu.config.AppConfig;
import tech.yixiyun.framework.kuafu.config.ConfigKey;
import tech.yixiyun.framework.kuafu.db.DbException;
import tech.yixiyun.framework.kuafu.db.DbKit;
import tech.yixiyun.framework.kuafu.db.datasource.DataSourceContext;
import tech.yixiyun.framework.kuafu.db.transaction.TransactionLevel;
import tech.yixiyun.framework.kuafu.log.LOGGER;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

/**
 * 数据库会话管理类
 *
 * @author Yixiyun
 * @version 1.0
 * @date 2021-04-24 15:11
 */
public class DbSession {




    /**
     * 从不同数据源获取的链接，每个数据源只能保持一个链接。
     * Entry中存放的是事务等级和链接
     */
    private Map<String, ConnectionItem> connectionMap = null;



    /**
     * 每个会话可能涉及多个事务，多个事务可能等级还不同。为了安全，一旦后面遇到的事务等级比之前的高，会统一把当前
     * 会话的所有事务等级都调高至当前最高的等级。这个就是用来记录当前最高的事务等级。
     */
    private TransactionLevel level = TransactionLevel.NONE;


    /**
     * 开启事务，使用配置文件中的默认事务隔离级别
     */
    public void openTransaction() {
        this.setLevel(AppConfig.getAsEnum(ConfigKey.DB_TRANSACTION_DEFAULTLEVEL, TransactionLevel.class));
    }

    /**
     * 开启事务，使用指定的事务隔离级别。注意别用NONE，它代表不开启事务
     * @param level
     */
    public void openTransaction(TransactionLevel level) {
        this.setLevel(level);
    }


    /**
     * 当前整个会话的事务等级
     * @return
     */
    public TransactionLevel getTransactionLevel() {
        return level;
    }

    /**
     * 更新会话所有事务的等级，只有当新的等级比之前的等级高，才会更新当前所有事务
     * @param level
     */
    public void setLevel(TransactionLevel level) {
        if (level.getLevel() > 0 && level.getLevel() > this.level.getLevel() && connectionMap != null && connectionMap.isEmpty() == false) {
            connectionMap.values().forEach(item -> {
                try {
                    item.updateTransactionIsolation(level.getLevel());
                } catch (SQLException e) {
                    throw new DbException(e);
                }
            });
        }
        this.level = level;
    }



    /**
     * 获取数据库连接，根据数据源名称获取
     * @param dataSourceName
     * @return
     */
    public Connection getConnection(String dataSourceName) {
        if (connectionMap == null) connectionMap = new HashMap<>();

        ConnectionItem item = connectionMap.get(dataSourceName);
        if (item == null) {
            Connection connection = DataSourceContext.getDataSource(dataSourceName).getConnection();
            try {
                //声明了事务
                if (level != TransactionLevel.DEFAULT && level != TransactionLevel.NONE) {
                    connection.setTransactionIsolation(level.getLevel());

                }
            } catch (SQLException e) {
                throw new DbException("设置数据库连接发生异常", e);
            }
            item = new ConnectionItem(connection);
            connectionMap.put(dataSourceName, item);
        }

        if (level != TransactionLevel.NONE) {
            DbKit.beginTransaction(item.connection);
        }
        return item.connection;
    }

    /**
     * 提交当前会话中的所有事务
     */
    public void commit() {
        if (connectionMap == null) return;
        connectionMap.values().forEach(item -> DbKit.commitTransaction(item.connection));
    }



    /**
     * 回滚所有连接
     */
    public void rollback() {
        if (connectionMap == null) return;
        connectionMap.values().forEach(item -> DbKit.rollbackTransaction(item.connection));
    }

    /**
     * 将连接重置为自动提交 和 默认的事务等级
     */
    public void reset() {
        this.level = TransactionLevel.NONE;
        if (connectionMap == null) return;
        connectionMap.values().forEach(item -> {
            try {
                item.connection.setAutoCommit(false);
                item.connection.setTransactionIsolation(item.defaultLevel);
//                LOGGER.trace("连接已重置：{}", item.connection);
            } catch (Exception e) {
                throw new DbException("重置数据库连接时发生异常", e);
            }
        });

    }


    /**
     * 关闭会话中的所有连接，该方法不负责提交、回滚事务，所以执行该方法前，请自行确认已提交事务或者回滚事务
     */
    public void clear() {
        if (connectionMap == null || connectionMap.isEmpty()) return;
        try {
            connectionMap.values().forEach(item -> {

                DbKit.closeConnection(item.connection);
            });
        } finally {
            connectionMap.clear();
        }
    }

    /**
     * 关闭会话中某个数据源的连接，该方法不负责提交、回滚事务，所以执行该方法前，请自行确认已提交事务或者回滚事务
     * @param dataSourceName
     */
    public void clear(String dataSourceName) throws SQLException {
        if (connectionMap == null || connectionMap.isEmpty()) return;
        ConnectionItem item = connectionMap.remove(dataSourceName);
        if (item != null) {
            item.connection.close();
        }
    }



    private class ConnectionItem {
        public int defaultLevel; //数据库默认隔离级别,reset时使用
        public Connection connection;

        public ConnectionItem(Connection connection) {
            try {
                this.defaultLevel = connection.getTransactionIsolation();
            } catch (SQLException e) {
                LOGGER.error("获取数据库连接事务隔离级别失败", e);
            }
            this.connection = connection;
        }

        /**
         * 更新连接的事务隔离级别
         * @param level
         */
        public void updateTransactionIsolation(int level) throws SQLException {
            if (this.connection.getTransactionIsolation() < level) {
                this.connection.setTransactionIsolation(level);
            }
        }
    }


}
